using System;
using UnityEngine;
using UnityEngine.UI;

[DisallowMultipleComponent]
[RequireComponent(typeof(GridLayoutGroup))]
public class MyLoopGrid : MonoBehaviour
{
    private GridLayoutGroup grid;
    private RectTransform mRect;
    private ScrollRect scrollRect;

    public GameObject prefabItem;
    Action<GameObject, int> OnItemChange;

    /// </summary>
    /// 当前页要显示的对象上表
    /// <summary>
    public int TopIndex;
    /// <summary>
    ///当前列表Item总数 
    /// </summary>
    private int maxItemNum;
    /// <summary>
    /// 列表布局有几行或者几列 
    /// </summary>
    private int maxPerCount;
    /// <summary>
    /// 当前列表总行数或者总列数
    /// </summary>
    private int sceneRanks;
    /// <summary>
    /// Item的 width 或者 height
    /// </summary>
    private float distance;

    /// <summary>
    /// 当前最小的索引
    /// </summary>
    private int minIdx;
    /// <summary>
    /// 当前最大的索引
    /// </summary>
    private int maxIdx;

    private float offsetH;
    private float offsetV;
    private int dataCount;
    private int maxRanks;

    void Awake()
    {
        grid = GetComponent<GridLayoutGroup>();
        mRect = GetComponent<RectTransform>();
        mRect.anchorMin = new Vector2(0, 0);
        mRect.anchorMax = new Vector2(0, 1);

        scrollRect = GetComponentInParent<ScrollRect>();
        scrollRect.viewport.pivot = new Vector2(0f, 1f);
        scrollRect.vertical = !scrollRect.horizontal;
        scrollRect.movementType = ScrollRect.MovementType.Elastic;

        maxPerCount = grid.constraintCount;
        if (maxPerCount <= 0) { maxPerCount = 1; }

        distance = grid.cellSize.x + grid.spacing.x;
        offsetH = grid.padding.left + grid.padding.right;
        float num = scrollRect.viewport.rect.width - offsetH;

        if (scrollRect.vertical)
        {
            mRect.anchorMin = new Vector2(0, 1);
            mRect.anchorMax = new Vector2(1, 1);

            distance = grid.cellSize.y + grid.spacing.y;
            offsetV = grid.padding.top + grid.padding.bottom;
            num = scrollRect.viewport.rect.height - offsetV;
        }

        float ranks = num / distance;
        sceneRanks = Mathf.FloorToInt(ranks) + 2;
        maxItemNum = sceneRanks * maxPerCount;
        if (prefabItem != null) { InitData(); }
    }

    void Update()
    {
        OnValueChanged(Vector2.zero);
    }

    void OnValueChanged(Vector2 value)
    {
        if (dataCount <= maxItemNum) { return; }
        float curIdx = Mathf.Abs(mRect.localPosition.x / distance);

        if (scrollRect.vertical)
        {
            curIdx = Mathf.Abs(mRect.localPosition.y / distance);
        }

        if (curIdx <= minIdx)
        {
            if (minIdx - curIdx >= sceneRanks)
            {
                minIdx = (int)curIdx;
                maxIdx = minIdx + sceneRanks;
            }
        }
        else if (curIdx >= minIdx - 2)
        {
            if (curIdx - minIdx >= sceneRanks)
            {
                int upLimit = maxRanks - sceneRanks;
                if (curIdx <= upLimit)
                {
                    maxIdx = maxRanks;
                    minIdx = upLimit;
                }
            }
        }

        for (int i = 0; i < transform.childCount; i++)
        {
            var tf = transform.GetChild(i);
            float page = i / maxPerCount + curIdx;
            int curPage = Mathf.FloorToInt(page);

            tf.localPosition = GetLocalPos(tf, curPage);
            int startIdx = Mathf.RoundToInt(curIdx) * maxPerCount;
            UpdateItem(tf, startIdx + i);
        }
    }

    Vector3 GetLocalPos(Transform tf, int index)
    {
        if (tf == null) { return Vector3.zero; }
        Vector3 pos = tf.localPosition;

        if (scrollRect.horizontal)
        {
            pos = new Vector3(index * distance + grid.cellSize.x / 2, pos.y);
        }
        else
        {
            pos = new Vector3(pos.x, -index * distance - grid.cellSize.y / 2);
        }
        return pos;
    }

    void UpdateItem(Transform tf, int idx)
    {
        if (tf != null)
        {
            bool bShow = idx >= 0 && idx < dataCount;
            tf.name = idx.ToString();
            tf.gameObject.SetActive(bShow);
            OnItemChange?.Invoke(tf.gameObject, idx);
        }
    }

    public void InitData()
    {
        prefabItem.SetActive(false);
        for (int i = 0; i < maxItemNum; i++)
        {
            var go = Instantiate(prefabItem, transform);
            go.name = i.ToString();
        }
    }

    public void SetTopIndex(int topIdx)
    {
        if (topIdx > 0 && topIdx <= dataCount)
        {
            TopIndex = topIdx; Rect rect = scrollRect.viewport.rect;
            int page = Mathf.CeilToInt(topIdx / maxPerCount) - 1;
            Vector3 pos = scrollRect.content.anchoredPosition;

            if (scrollRect.horizontal)
            {
                pos.x = page * distance + grid.cellSize.x / 2;
            }
            else
            {
                pos.y = page * distance - grid.cellSize.y / 2;
            }
            scrollRect.content.anchoredPosition = pos;
        }
    }

    public void UpDataList(int count, int topIdx = 0)
    {
        dataCount = count; minIdx = 0;
        maxRanks = count / maxPerCount;

        maxIdx = maxRanks > sceneRanks ? sceneRanks : maxRanks;
        Vector2 sizeDelta = scrollRect.content.sizeDelta;
        if (scrollRect.horizontal)
        {
            sizeDelta.x = offsetH + maxRanks * distance - grid.spacing.x;
        }
        else
        {
            sizeDelta.y = offsetV + maxRanks * distance - grid.spacing.y;
        }

        scrollRect.content.sizeDelta = sizeDelta;
        for (int i = 0; i < transform.childCount; i++)
        {
            Transform tf = transform.GetChild(i);
            if (i < count)
            {
                UpdateItem(tf, i);
            }
            tf.gameObject.SetActive(i < count);
        }
        SetTopIndex(topIdx);
    }
}